/*
 * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <mmio.h>
#include <utils_def.h>

#include "uniphier.h"

#define UNIPHIER_ROM_RSV3        0x5980120c

#define UNIPHIER_STMBE2COM        0x5f800030
#define UNIPHIER_BETOSTMIRQ0PT        0x5f800070

#define UNIPHIER_SCP_READY_MAGIC    0x0000b6a5

#define UNIPHIER_SCP_PACKET_START    0xA0
#define UNIPHIER_SCP_PACKET_END        0xA5
#define UNIPHIER_SCP_PACKET_ESC        0xA6
#define UNIPHIER_SCP_IS_CTRL_CODE(c)    (0xA0 <= (c) && (c) <= 0xA6)

int uniphier_scp_is_running(void)
{
    return mmio_read_32(UNIPHIER_STMBE2COM) == UNIPHIER_SCP_READY_MAGIC;
}

void uniphier_scp_start(void)
{
    uint32_t tmp;

    mmio_write_32(UNIPHIER_STMBE2COM + 4, UNIPHIER_SCP_BASE);
    mmio_write_32(UNIPHIER_STMBE2COM, UNIPHIER_SCP_READY_MAGIC);

    do {
        tmp = mmio_read_32(UNIPHIER_ROM_RSV3);
    } while (!(tmp & BIT(8)));

    mmio_write_32(UNIPHIER_ROM_RSV3, tmp | BIT(9));
}

static void uniphier_scp_send_packet(const uint8_t *packet, int packet_len)
{
    uintptr_t reg = UNIPHIER_STMBE2COM;
    uint32_t word;
    int len, i;

    while (packet_len) {
        len = MIN(packet_len, 4);
        word = 0;

        for (i = 0; i < len; i++)
            word |= *packet++ << (8 * i);

        mmio_write_32(reg, word);
        reg += 4;
        packet_len -= len;
    }

    mmio_write_8(UNIPHIER_BETOSTMIRQ0PT, 0x55);
}

static void uniphier_scp_send_cmd(const uint8_t *cmd, int cmd_len)
{
    uint8_t packet[32];    /* long enough */
    uint8_t *p = packet;
    uint8_t c;
    int i;

    *p++ = UNIPHIER_SCP_PACKET_START;
    *p++ = cmd_len;

    for (i = 0; i < cmd_len; i++) {
        c = *cmd++;
        if (UNIPHIER_SCP_IS_CTRL_CODE(c)) {
            *p++ = UNIPHIER_SCP_PACKET_ESC;
            *p++ = c ^ BIT(7);
        } else {
            *p++ = c;
        }
    }

    *p++ = UNIPHIER_SCP_PACKET_END;

    uniphier_scp_send_packet(packet, p - packet);
}

#define UNIPHIER_SCP_CMD(name, ...)                    \
static const uint8_t __uniphier_scp_##name##_cmd[] = {            \
    __VA_ARGS__                            \
};                                    \
void uniphier_scp_##name(void)                        \
{                                    \
    uniphier_scp_send_cmd(__uniphier_scp_##name##_cmd,        \
                  ARRAY_SIZE(__uniphier_scp_##name##_cmd));    \
}

UNIPHIER_SCP_CMD(open_com, 0x00, 0x00, 0x05)
UNIPHIER_SCP_CMD(system_off, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x01)
UNIPHIER_SCP_CMD(system_reset, 0x00, 0x02, 0x00)
